package com.yvon.boot.jwt.utils;


import cn.hutool.core.convert.Convert;
import cn.hutool.json.JSONUtil;
import com.yvon.boot.jwt.properties.JwtProperties;
import com.yvon.boot.jwt.model.AuthInfo;
import com.yvon.boot.jwt.model.Token;
import io.jsonwebtoken.Claims;
import lombok.AllArgsConstructor;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import static com.yvon.boot.jwt.context.JwtConstants.*;

/**
 * 认证工具类
 *
 * @author : Yvon
 * @since : 2021-09-02
 */
@AllArgsConstructor
public class TokenUtil {
    /**
     * 认证服务端使用，如 authority-server
     * 生成和 解析token
     */
    private final JwtProperties jwtProperties;


    /**
     * 创建认证token
     *
     * @param map          地图
     * @param expireMillis 到期,米尔斯
     * @return AuthInfo
     * @author : Yvon / 2021-11-26
     */
    public AuthInfo createAuthInfo(Map<String, Object> map,  Long expireMillis) {
        if (expireMillis == null || expireMillis <= 0) {
            expireMillis = jwtProperties.getExpire();
        }

        //设置jwt参数
        Map<String, Object> param = new HashMap<>(16);
        param.put(JWT_KEY_TOKEN_TYPE, BEARER_HEADER_KEY);
        param.putAll(map);

        Token token = JwtUtil.createJwt(param, expireMillis);

        AuthInfo authInfo = new AuthInfo();
        authInfo.setUsername((String) param.get(JWT_KEY_USERNAME));
        authInfo.setId((Long) param.get(JWT_KEY_USER_ID));
        authInfo.setTokenType(BEARER_HEADER_KEY);
        authInfo.setHeader(jwtProperties.getHeader());
        authInfo.setToken(token.getToken());
        authInfo.setExpire(token.getExpire());
        authInfo.setExpiration(token.getExpiration());
        authInfo.setRefreshToken(createRefreshToken(map).getToken());
        authInfo.setExpireMillis(expireMillis);
        return authInfo;
    }

    /**
     * 创建refreshToken
     *
     * @param map 地图
     * @return Token
     * @author : Yvon / 2021-11-26
     */
    private Token createRefreshToken(Map<String, Object> map) {
        Map<String, Object> param = new HashMap<>(16);
        param.put(JWT_KEY_TOKEN_TYPE, REFRESH_TOKEN_KEY);
        param.putAll(map);
        return JwtUtil.createJwt(param, jwtProperties.getRefreshExpire());
    }
    /**
     * 从令牌中获取用户名
     *
     * @param token 令牌
     * @return java.lang.String 用户名
     * @author : Yvon
     */
    public String getSubjectFromToken(String token) {
        // 获取subject
        Claims claims = JwtUtil.parseJwt(token, jwtProperties.getAllowedClockSkewSeconds());
        if (!BEARER_HEADER_KEY.equals(Convert.toStr(claims.get(JWT_KEY_TOKEN_TYPE)))) {
            throw new RuntimeException("不合法的token，请认真比对 token 的签名");
        }
        return Convert.toStr(claims.get(JWT_KEY_USERNAME));
    }

    /**
     * 从刷新令牌中获取用户名
     *
     * @param token 令牌
     * @return java.lang.String 用户名
     * @author : Yvon
     */
    public String getSubjectFromRefreshToken(String token) {
        // 获取subject
        Claims claims = JwtUtil.parseJwt(token, jwtProperties.getAllowedClockSkewSeconds());
        if (!REFRESH_TOKEN_KEY.equals(Convert.toStr(claims.get(JWT_KEY_TOKEN_TYPE)))) {
            throw new RuntimeException("不合法的token，请认真比对 token 的签名");
        }
        return Convert.toStr(claims.get(JWT_KEY_USERNAME));
    }

    /**
     * 解析token
     *
     * @param token token
     * @return 用户信息
     */
    public AuthInfo getAuthInfo(String token) {
        Claims claims = JwtUtil.getClaims(token, jwtProperties.getAllowedClockSkewSeconds());
        String tokenType = Convert.toStr(claims.get(JWT_KEY_TOKEN_TYPE));
        String username = Convert.toStr(claims.get(JWT_KEY_USERNAME));

        Date expiration = claims.getExpiration();
        return new AuthInfo().setToken(token)
                .setExpire(expiration != null ? expiration.getTime() : 0L)
                .setTokenType(tokenType).setUsername(username);
    }

    public <T> T parseToken(String token, Class<T> clazz) {
        Claims claims = JwtUtil.getClaims(token, jwtProperties.getAllowedClockSkewSeconds());
        return JSONUtil.toBean(JSONUtil.toJsonStr(claims), clazz);
    }

    /**
     * 解析刷新token
     *
     * @param token 待解析的token
     * @return 认证信息
     */
    public AuthInfo parseRefreshToken(String token) {
        Claims claims = JwtUtil.parseJwt(token, jwtProperties.getAllowedClockSkewSeconds());
        String tokenType = Convert.toStr(claims.get(JWT_KEY_TOKEN_TYPE));
        String username = Convert.toStr(claims.get(JWT_KEY_USERNAME));
        Date expiration = claims.getExpiration();
        return new AuthInfo().setToken(token)
                .setExpire(expiration != null ? expiration.getTime() : 0L)
                .setUsername(username)
                .setTokenType(tokenType);
    }

}
